/*
 * @(#)StandardAuthenticationHandler.java
 *
 * Copyright (c) 2003 DCIVision Ltd
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of DCIVision
 * Ltd ("Confidential Information").  You shall not disclose such Confidential
 * Information and shall use it only in accordance with the terms of the license
 * agreement you entered into with DCIVision Ltd.
 */
package com.dcivision.user.auth;

import java.sql.Connection;
import java.sql.Timestamp;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.dcivision.framework.ApplicationContainer;
import com.dcivision.framework.ApplicationException;
import com.dcivision.framework.Crypt;
import com.dcivision.framework.ErrorConstant;
import com.dcivision.framework.GlobalConstant;
import com.dcivision.framework.SessionContainer;
import com.dcivision.framework.SystemParameterConstant;
import com.dcivision.framework.SystemParameterFactory;
import com.dcivision.framework.Utility;
import com.dcivision.framework.bean.AuditLoginOut;
import com.dcivision.framework.dao.AuditLoginOutDAObject;
import com.dcivision.framework.web.LoginForm;
import com.dcivision.framework.PermissionManager;
import com.dcivision.ldap.core.LdapAuthenticationHandler;
import com.dcivision.user.bean.UserGroup;
import com.dcivision.user.bean.UserRecord;
import com.dcivision.user.bean.UserRole;
import com.dcivision.user.dao.UserGroupDAObject;
import com.dcivision.user.dao.UserRecordDAObject;
import com.dcivision.user.dao.UserRoleDAObject;

/**
  StandardAuthenticationHandler.java

  This class is providing an interface for all kind of authenication implementations.

    @author           Scott Tong
    @company          DCIVision Limited
    @creation date    16/07/2003
    @version          $Revision: 1.32.2.3 $
*/

public class StandardAuthenticationHandler implements AuthenticationHandler{

  private static final String USER_RECORD_ID = "ID";

  public static final String REVISION = "$Revision: 1.32.2.3 $";

  protected Log log = LogFactory.getLog(this.getClass().getName());

  public UserRecord Login(LoginForm loginForm, SessionContainer sessionCtn, HttpServletRequest request, Connection conn) throws ApplicationException {
    String loginName = loginForm.getLoginName();
    String loginPwd = loginForm.getLoginPwd();
    UserRecord userRecord = null;
    log.info("getConnection in LoginAction");
    try
    {
      UserRecordDAObject userRecordDAO = new UserRecordDAObject(sessionCtn, conn);

      userRecord = (UserRecord) userRecordDAO.getObjectByLoginName(loginName, GlobalConstant.RECORD_STATUS_ACTIVE);
      if ((userRecord != null && GlobalConstant.RECORD_STATUS_INACTIVE.equals(userRecord.getRecordStatus())) 
      || (userRecord != null && GlobalConstant.STATUS_INACTIVE.equals(userRecord.getStatus()))) {
        userRecord = null;
      }

      if (userRecord != null) {
        request.setAttribute(USER_RECORD_ID, userRecord.getID());
        log.info("There is userRecord in LoginAction");

        if (((ApplicationContainer) request.getSession().getServletContext().getAttribute(GlobalConstant.APPLICATION_CONTAINER_KEY)).isLogin(userRecord.getID())
            && !(SystemParameterFactory.getSystemParameter(SystemParameterConstant.ALLOW_DOUBLE_LOGIN)).equals("true")) {
          log.info("Double Login");
          throw new ApplicationException(ErrorConstant.LOGIN_DOUBLE_LOGIN);
        } else {
          if (GlobalConstant.STATUS_LOCKED.equals(userRecord.getStatus())) {
            log.info("Account is locked");
            throw new ApplicationException(ErrorConstant.LOGIN_ACCOUNT_LOCKED);
          } 
          //LDAP login begin
          boolean success=false;
          String loginMethod = SystemParameterFactory.getSystemParameter(SystemParameterConstant.LOGIN_METHOD);
          if( ( Utility.isEmpty(loginMethod) || loginMethod.equalsIgnoreCase(GlobalConstant.LOGIN_METHOD_PARADM))            
              || (userRecord.getID().intValue()==0))
          {
              String encrptedPwd = Crypt.encrypt(loginPwd, SystemParameterFactory.getSystemParameter(SystemParameterConstant.CRYPTO_SALT));
              success=encrptedPwd.equals(userRecord.getLoginPwd());
          }else if(loginMethod.equalsIgnoreCase(GlobalConstant.LOGIN_METHOD_LDAP) 
                || loginMethod.equalsIgnoreCase(GlobalConstant.LOGIN_METHOD_NTLM)){
            LdapAuthenticationHandler ldapAuthentication=new LdapAuthenticationHandler();
            success=ldapAuthentication.login(loginName,loginPwd);                    
          }
          //LDAP login end
  
          if (success) {
            initializeLoginUser(loginName, request, conn);
          } else {
            log.info("Wrong password in LoginAction");
            // Increment failed attempt.
            if (Utility.isEmpty(loginForm.getUpdateFailedAttemptFlag()) 
             || GlobalConstant.TRUE.equals(loginForm.getUpdateFailedAttemptFlag())) {
              int failedAttempt = (userRecord.getFailedAttempt() == null) ? 0 : userRecord.getFailedAttempt().intValue();
              userRecord.setFailedAttempt(new Integer(failedAttempt + 1));
              userRecordDAO.updateFailedAttempt(userRecord);
            }
            if (!Utility.isEmpty(userRecord.getFailedAttempt()) && !Utility.isEmpty(userRecord.getMaxAttempt())) {
                if (userRecord.getFailedAttempt().intValue() >= (userRecord.getMaxAttempt().intValue())) {
                  sessionCtn.setUserRecord(userRecord);
                  request.getSession().setAttribute(GlobalConstant.SESSION_CONTAINER_KEY, sessionCtn);
                  userRecord.setStatus(GlobalConstant.STATUS_LOCKED);
                  userRecord.setLoginPwd("");
                  userRecordDAO.updateObject(userRecord);
                  log.info("Failed attempt more than max attempt");
                  throw new ApplicationException(ErrorConstant.LOGIN_ACCOUNT_LOCKED_ATTEMPT,null,"" + (userRecord.getFailedAttempt().intValue()));
                }
            }
            throw new ApplicationException(ErrorConstant.LOGIN_WRONG_PASSWORD);
          }
        }
      } else {
        log.info("No user record found");
        throw new ApplicationException(ErrorConstant.LOGIN_NO_USER_RECORD);
      }
    }catch(ApplicationException e){
      logAuthenticationError(loginName, e, sessionCtn, request, conn);
      throw e;
    }  
    return userRecord;
  }


  public void Logout(SessionContainer sessionContainer, HttpServletRequest request) {
    /*
        ((ApplicationContainer)request.getSession().getServletContext().getAttribute(
    GlobalConstant.APPLICATION_CONTAINER_KEY)).logoutUser(sessionContainer.getUserRecordID());
    */
  }

  /**
   * Method to log authentication error into AUDIT_LOGIN_LOGOUT table
   * Errors to log:
   * 1) Wrong password
   * 2) Account locked
   * 3) Input wrong password attempts 
   * 4) Duplicate login
   * 5) No right to change password
   */
  protected void logAuthenticationError(String loginName, ApplicationException appEx, SessionContainer sessionCtn, HttpServletRequest request, Connection conn)
  throws ApplicationException{
      String auditStatus = null;
      if((ErrorConstant.LOGIN_WRONG_PASSWORD).equals(appEx.getMsgCode())) {
        auditStatus = AuditLoginOut.STATUS_WRONG_PASSWORD;
      } else if((ErrorConstant.LOGIN_ACCOUNT_LOCKED).equals(appEx.getMsgCode())) {
        auditStatus = AuditLoginOut.STATUS_ACCOUNT_LOCKED;
      } else if((ErrorConstant.LOGIN_ACCOUNT_LOCKED_ATTEMPT).equals(appEx.getMsgCode())) {
        auditStatus = AuditLoginOut.STATUS_ATTEMPT_EXCESS;
      } else if((ErrorConstant.LOGIN_DOUBLE_LOGIN).equals(appEx.getMsgCode())) {
        auditStatus = AuditLoginOut.STATUS_DUPLICATE_LOGIN;
      } else if((ErrorConstant.LOGIN_PASSWORD_EXPIRED).equals(appEx.getMsgCode())) {
        auditStatus = AuditLoginOut.STATUS_PASSWORD_EXPIRED;        
      } else if((ErrorConstant.LOGIN_SESSION_EXPIRED).equals(appEx.getMsgCode())) {
        //do nothing here
      } else if (ErrorConstant.LOGIN_PASSWORD_EXPIRED_AND_NO_PERMISSION_CHANGE.equals(appEx.getMsgCode())) {
        auditStatus = AuditLoginOut.STATUS_ACCOUNT_LOCKED;
      }
      if(!Utility.isEmpty(auditStatus)){
        UserRecordDAObject userRecordDAO = new UserRecordDAObject(sessionCtn, conn);
        UserRecord userRecord = (UserRecord)userRecordDAO.getObjectByLoginName(loginName);
        String userIP = request.getRemoteAddr();
        auditLogin(conn, request, userRecord.getID(), userIP, auditStatus,request.getSession().getId(), userRecord.getLoginName());              
      }      
  } 
   
  /**
   * Method to iniatlize a user account after login success
   * 1) Load locale information and store into session
   * 2) Load user membership (group / role) information and store into session
   * 3) Reset fail login attempt
   * 4) Initialize PermissionManager for that login session
   * 5) Reset password expiry information
   * 6) Reset previous session temporary objects (tree view, clipboard cache etc)
   * 7) Assign a pre-defined screen width (1024 pixel)
   */
  public void initializeLoginUser(String loginName, HttpServletRequest request, Connection conn) throws PasswordExpiredException, ApplicationException{
    Timestamp now = new Timestamp(System.currentTimeMillis());
    Timestamp expiryDay = now;
  
    SessionContainer sessionCtn = new SessionContainer();
    UserRecordDAObject userRecordDAO = new UserRecordDAObject(sessionCtn, conn);
    
    //log.info("login successfully in LoginAction");
    UserRecord userRecord = (UserRecord) userRecordDAO.getObjectByLoginName(loginName, GlobalConstant.RECORD_STATUS_ACTIVE);
    
    // Set the locale if the user has locale preference.
    if (userRecord.getLocale() != null) {
      request.getSession().setAttribute(org.apache.struts.Globals.LOCALE_KEY, Utility.getLocaleByString(userRecord.getLocale()));
    }
    
    // Get User Groups Information.
    UserGroupDAObject userGroupDAO = new UserGroupDAObject(sessionCtn, conn);
    List userGroups = userGroupDAO.getListByUserRecordIDGroupType(userRecord.getID(), com.dcivision.user.bean.UserGroup.GROUP_TYPE_PUBLIC);
    sessionCtn.getPermissionManager().setUserGroups(userGroups);
    
    // Get User Roles and Group Role Information (Union them with no duplicate roles).
    UserRoleDAObject userRoleDAO = new UserRoleDAObject(sessionCtn, conn);
    List userRoles = userRoleDAO.getListByUserRecordID(userRecord.getID());
    
    for (int i = 0; i < userGroups.size(); i++) {
      UserGroup tmpUserGroup = (UserGroup) userGroups.get(i);
      List userRolesByGroup = userRoleDAO.getListByUserGroupID(tmpUserGroup.getID());
      for (int j = 0; j < userRolesByGroup.size(); j++) {
        if (!userRoles.contains(userRolesByGroup.get(j))) {
          userRoles.add(userRolesByGroup.get(j));
        }
      }
    }
    
    sessionCtn.getPermissionManager().setUserRoles(userRoles);
    
    // Reset fail attempt to 0.
    //if (Utility.isEmpty(loginForm.getUpdateFailedAttemptFlag()) || GlobalConstant.TRUE.equals(loginForm.getUpdateFailedAttemptFlag())) {
      userRecord.setFailedAttempt(new Integer(0));
      userRecordDAO.updateFailedAttempt(userRecord);
    //}
    
    // Log the login succeed.
    sessionCtn.setUserRecord(userRecord);
    
    Integer pwdExpiryDay = userRecord.getPwdExpiryDay();
    Timestamp lastPwdUpdateDate = userRecord.getLastPwdUpdateDate();
    if (pwdExpiryDay != null && lastPwdUpdateDate != null) {
      expiryDay = new Timestamp(lastPwdUpdateDate.getTime() + ((pwdExpiryDay.intValue()) * 24L * 60L * 60L * 1000L));
    }
    request.getSession().setAttribute(GlobalConstant.SESSION_CONTAINER_KEY, sessionCtn);
    log.debug("lastPwdUpdateDate is " + lastPwdUpdateDate);
    log.debug("pwdExpiryDay is " + pwdExpiryDay);
    log.debug("expiryDay is " + expiryDay);
    //log.debug("now is " + now);
    if (pwdExpiryDay != null) {
      if (now.after(expiryDay)) {
        log.info("Password is expired");
        if (GlobalConstant.FALSE.equals(userRecord.getModifyLoginPwd())) {
          // Lock account if password is expired and user have no permission to change the password.
          userRecord.setStatus(GlobalConstant.STATUS_LOCKED); 
          userRecordDAO.updateObject(userRecord);
          throw new ApplicationException(ErrorConstant.LOGIN_PASSWORD_EXPIRED_AND_NO_PERMISSION_CHANGE);
        } else {
          sessionCtn.setLoginStatus(AuditLoginOut.STATUS_PASSWORD_EXPIRED);
        }
      } else {
        log.info("Password is not expired");
      }
    } else {
      log.info("Password is not expired");
    }  
    
    //store session container into user session with key GlobalConstant.SESSION_CONTAINER_KEY    
    request.getSession().setAttribute(GlobalConstant.SESSION_CONTAINER_KEY, sessionCtn);
    
    //
    String userIP = request.getRemoteAddr();
    AuditLoginOut audit = auditLogin(conn, request, userRecord.getID(), userIP, 
                                     AuditLoginOut.STATUS_LOGIN_SUCCESS, request.getSession().getId(), userRecord.getLoginName());

    sessionCtn.setUserRecord(userRecord);
    sessionCtn.setLoginTime(audit.getLoginDatetime());
    sessionCtn.setUserIPAddress(userIP);
    //store user predefined locale preference into session    
    sessionCtn.setLocale(Utility.getLocaleByString(userRecord.getLocale()));

    //throws PasswordExpiredException if user's password is expired
    if (AuditLoginOut.STATUS_PASSWORD_EXPIRED.equals(sessionCtn.getLoginStatus())) {      
      throw new PasswordExpiredException(userRecord.getID().toString());
    }
    sessionCtn.setLoginStatus(AuditLoginOut.STATUS_LOGIN_SUCCESS);

    //reset the clipboard items for each login
    if (!Utility.isEmpty(request.getSession().getAttribute("DMS_CLIPBOARD"))){
      List clipBoardList = (List)request.getSession().getAttribute("DMS_CLIPBOARD");
      clipBoardList.removeAll(clipBoardList);
      request.getSession().setAttribute("DMS_CLIPBOARD", clipBoardList);
    }
    //add sessionCtn to PermissionManager sessionctn listener
    PermissionManager.addPermissionListener(sessionCtn);
    
    //reset the clipboard items for each login
    request.getSession(true).removeAttribute("sessEmailList");
    request.getSession(true).removeAttribute("folder");        
    
    request.getSession(true).removeAttribute("folderTreeParentID");
    request.getSession(true).removeAttribute("publicfolderTreeParentID");     
    
    request.getSession().setAttribute(GlobalConstant.CLIENT_SCREEN_WIDTH_KEY, "1024");
  }

  protected AuditLoginOut auditLogin(Connection conn, HttpServletRequest request, Integer userID, String userIP, String status, String sessionID, String loginName) 
  throws ApplicationException {
    AuditLoginOutDAObject auditLoginOutDAObject = new AuditLoginOutDAObject(null, conn);
    AuditLoginOut auditLoginOut = new AuditLoginOut();
    auditLoginOut.setLoginName(loginName);
    auditLoginOut.setUserRecordID(userID);
    auditLoginOut.setIPAddress(userIP);
    auditLoginOut.setSessionID(sessionID);
    auditLoginOut.setLoginDatetime(new Timestamp(System.currentTimeMillis()));
    auditLoginOut.setLoginStatus(status);
    
    /*If the login action happen following exception,set "timeOutFlag" is "E",
    "listAuditLoginOut.jsp" need check this property for display.*/
    boolean timeOutFlag=status.equals(AuditLoginOut.STATUS_WRONG_PASSWORD) || 
                        status.equals(AuditLoginOut.STATUS_ACCOUNT_LOCKED) ||
                        status.equals(AuditLoginOut.STATUS_ATTEMPT_EXCESS) ||
                        status.equals(AuditLoginOut.STATUS_DUPLICATE_LOGIN) ||
                        status.equals(AuditLoginOut.STATUS_PASSWORD_EXPIRED) ||
                        status.equals(AuditLoginOut.STATUS_ACCOUNT_LOCKED);
    if(timeOutFlag){
      auditLoginOut.setTimeoutFlag(GlobalConstant.ERROR);
    }
    
    if (AuditLoginOut.STATUS_LOGIN_SUCCESS.equals(status)){
    	((ApplicationContainer)request.getSession().getServletContext().getAttribute(GlobalConstant.APPLICATION_CONTAINER_KEY)).loginUser(auditLoginOut);
    }    
    auditLoginOut = (AuditLoginOut)auditLoginOutDAObject.insertObject(auditLoginOut);
    try {
      conn.commit();
    } catch (Exception e) {
      throw new ApplicationException(ErrorConstant.DB_COMMIT_ERROR);
    }
    return auditLoginOut;
  }  

  public void retrieveUserGroupList(SessionContainer sessionContainer) {

  }

  public void retrieveUserRoleList(SessionContainer sessionContainer) {

  }

  public void insertUserRecord(UserRecord userRecord, SessionContainer sessionContainer) {

  }

  public void updateUserRecord(UserRecord userRecord, SessionContainer sessionContainer) {

  }

  public void deleteUserRecord(UserRecord userRecord, SessionContainer sessionContainer) {

  }

  public void insertUserGroup(UserGroup userGroup, SessionContainer sessionContainer) {

  }

  public void updateUserGroup(UserGroup userGroup, SessionContainer sessionContainer) {

  }

  public void deleteUserGroup(UserGroup userGroup, SessionContainer sessionContainer) {

  }

  public void insertUserRole(UserRole userRole, SessionContainer sessionContainer) {

  }

  public void updateUserRole(UserRole userRole, SessionContainer sessionContainer) {

  }

  public void deleteUserRole(UserRole userRole, SessionContainer sessionContainer) {

  }

}